package com.maixy.studyonlineapi.utils;

import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author HuangLinWei
 * @since 2019/11/28
 */
public final class LocalDateUtils {

    public static final String SIMPLE_DATE_FORMAT = "yyyy-MM-dd";
    public static final String SLASH_DATE_FORMAT = "yyyy/MM/dd";
    public static final String DOT_DATE_FORMAT = "yyyy.MM.dd";


    private static final ZoneId DEFAULT_ZONE = ZoneId.systemDefault();
    private static final DateTimeFormatter SIMPLE_DATE_FORMATTER = DateTimeFormatter.ofPattern(SIMPLE_DATE_FORMAT);
    private static final DateTimeFormatter SLASH_DATE_FORMAT_FORMATTER = DateTimeFormatter.ofPattern(SLASH_DATE_FORMAT);


    private LocalDateUtils() {

    }

    public static String format(LocalDate localDate) {
        return SIMPLE_DATE_FORMATTER.format(localDate);
    }

    public static String format(Date date) {
        LocalDate localDate = dateToLocalDate(date);
        return format(localDate);
    }

    public static String format(LocalDate localDate, String pattern) {
        DateTimeFormatter dateTimeFormatterByPattern = getDateTimeFormatterByPattern(pattern);
        return dateTimeFormatterByPattern.format(localDate);
    }

    public static LocalDate parse(String date) {
        return LocalDate.parse(date, SIMPLE_DATE_FORMATTER);
    }

    public static Date parseToDate(String date) {
        LocalDate localDate = LocalDate.parse(date, SIMPLE_DATE_FORMATTER);
        return toDate(localDate);
    }

    public static Date toDate(LocalDate localDate){
        Instant instant = localDate.atStartOfDay().atZone(DEFAULT_ZONE).toInstant();
        return Date.from(instant);
    }

    public static Long toTimestamp(LocalDate localDate) {
        return toDate(localDate).getTime();
    }

    public static LocalDate parse(String date, String pattern) {
        DateTimeFormatter dateTimeFormatterByPattern = getDateTimeFormatterByPattern(pattern);
        return LocalDate.parse(date, dateTimeFormatterByPattern);
    }

    public static LocalDate dateToLocalDate(Date date) {
        return date.toInstant().atZone(DEFAULT_ZONE).toLocalDate();
    }

    public static Date localDateToDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(DEFAULT_ZONE).toInstant());
    }

    public static LocalDate of(long timestamp) {
        return Instant.ofEpochMilli(timestamp).atZone(DEFAULT_ZONE).toLocalDate();
    }

    public static LocalDate of(Date date) {
        return of(date.getTime());
    }

    public static DateTimeFormatter getDateTimeFormatterByPattern(String pattern) {
        switch (pattern) {
            case SLASH_DATE_FORMAT:
                return SLASH_DATE_FORMAT_FORMATTER;
            default:
                return DateTimeFormatter.ofPattern(pattern);
        }
    }

    public static LocalDate getMondayOfWeek(LocalDate date) {
        int dayOfWeek = date.getDayOfWeek().getValue();
        if(1 == dayOfWeek) {
            return date.atStartOfDay().toLocalDate();
        }
        return date.minusDays(dayOfWeek - 1);
    }

    public static LocalDate getSundayOfWeek(LocalDate date) {
        int dayOfWeek = date.getDayOfWeek().getValue();
        if(7 == dayOfWeek) {
            return date.atStartOfDay().toLocalDate();
        }
        return date.plusDays(7 - dayOfWeek);
    }

    /**
     * 判断是否相同一天
     *
     * @param localDate 日期
     * @param timestamp 时间戳
     * @return boolean
     * @author HuangLinWei
     * @since 2020/7/1
     */
    public static boolean isSameDay(LocalDate localDate, Long timestamp) {
        return isSameDay(localDate, Instant.ofEpochMilli(timestamp)
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime());
    }

    /**
     * 判断是否相同一天
     *
     * @param localDate 日期
     * @param localDateTime 时间
     * @return boolean
     * @author HuangLinWei
     * @since 2020/7/1
     */
    public static boolean isSameDay(LocalDate localDate, LocalDateTime localDateTime) {
        return localDate.compareTo(localDateTime.toLocalDate()) == 0;
    }

    public static LocalDateTime startOfDate(LocalDate localDate) {
        return localDate.atStartOfDay(DEFAULT_ZONE).toLocalDateTime();
    }

    /**
     * 根据日期获得这一周的开始日期
     * @param localDate 当前日期
     * @return LocalDate
     */
    public static LocalDate getStartDateOfWeek(LocalDate localDate) {
        DayOfWeek week = localDate.getDayOfWeek();
        int value = week.getValue();
        return localDate.minusDays(value - 1);
    }

    /**
     * 根据日期获得这一周的结束日期
     * @param localDate 当前日期
     * @return LocalDate
     */
    public static LocalDate getEndDateOfWeek(LocalDate localDate) {
        DayOfWeek week = localDate.getDayOfWeek();
        int value = week.getValue();
        return localDate.plusDays(7 - value);
    }

    /**
     * 获得两天之间的日期列表
     * @param start 开始时间
     * @param end 结束时间
     * @return 日期列表
     */
    public static List<LocalDate> getBetweenLocalDate(String start, String end) {
        List<LocalDate> list = new ArrayList<>();
        LocalDate localDateStart = LocalDateUtils.parse(start);
        LocalDate localDateEnd = LocalDateUtils.parse(end);
        long distance = ChronoUnit.DAYS.between(localDateStart, localDateEnd);
        if (distance < 0) {
            return list;
        }
        //开始的日期一直+1 直到distance + 1结束，然后遍历出结果
        Stream.iterate(localDateStart, d -> d.plusDays(1)).limit(distance + 1).forEach(list::add);
        return list;
    }
}
